/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2006
* Sleepycat Software. All rights reserved.
*
* $Id: DbLsn.java,v 1.1 2006/05/06 09:00:31 ckaestne Exp $
*/
package com.sleepycat.je.utilint;
import java.util.Arrays;
import com.sleepycat.je.log.FileManager;
import com.sleepycat.je.log.LogReadable;
import com.sleepycat.je.tree.TreeUtils;
/**
* DbLsn is a class that operates on Log Sequence Numbers (LSNs). An LSN is a
* long comprised of a file number (32b) and offset within that file (32b)
* which references a unique record in the database environment log. While
* LSNs are represented as long's, we operate on them using an abstraction and
* return longs from these methods so that we don't have to worry about the
* lack of unsigned quantities.
*/
public class DbLsn {
static final long INT_MASK = 0xFFFFFFFFL;
public static final long MAX_FILE_OFFSET = 0xFFFFFFFFL;
public static final long NULL_LSN = -1;
private DbLsn() {
}
public static long makeLsn(long fileNumber, long fileOffset) {
return fileOffset & INT_MASK |
((fileNumber & INT_MASK) << 32);
}
public static long longToLsn(Long lsn) {
if (lsn == null) {
return NULL_LSN;
}
return lsn.longValue();
}
/**
* Return the file number for this DbLsn.
* @return the number for this DbLsn.
*/
public static long getFileNumber(long lsn) {
return (lsn >> 32) & INT_MASK;
}
/**
* Return the file offset for this DbLsn.
* @return the offset for this DbLsn.
*/
public static long getFileOffset(long lsn) {
return (lsn & INT_MASK);
}
private static int compareLong(long l1, long l2) {
if (l1 < l2) {
return -1;
} else if (l1 > l2) {
return 1;
} else {
return 0;
}
}
public static int compareTo(long lsn1, long lsn2) {
if (lsn1 == NULL_LSN ||
lsn2 == NULL_LSN) {
throw new NullPointerException();
}
long fileNumber1 = getFileNumber(lsn1);
long fileNumber2 = getFileNumber(lsn2);
if (fileNumber1 == fileNumber2) {
return compareLong(getFileOffset(lsn1), getFileOffset(lsn2));
} else {
return compareLong(fileNumber1, fileNumber2);
}
}
public static String toString(long lsn) {
return "<DbLsn val=\"0x" +
Long.toHexString(getFileNumber(lsn)) +
"/0x" +
Long.toHexString(getFileOffset(lsn)) +
"\"/>";
}
public static String getNoFormatString(long lsn) {
return "0x" + Long.toHexString(getFileNumber(lsn)) + "/0x" +
Long.toHexString(getFileOffset(lsn));
}
public static String dumpString(long lsn, int nSpaces) {
StringBuffer sb = new StringBuffer();
sb.append(TreeUtils.indent(nSpaces));
sb.append(toString(lsn));
return sb.toString();
}
/**
* Return the logsize in bytes between these two LSNs. This is an
* approximation; the logs might actually be a little more or less in
* size. This assumes that no log files have been cleaned.
*/
public static long getNoCleaningDistance(long thisLsn,
long otherLsn,
long logFileSize) {
long diff = 0;
assert thisLsn != NULL_LSN;
/* First figure out how many files lay between the two. */
long myFile = getFileNumber(thisLsn);
if (otherLsn == NULL_LSN) {
otherLsn = 0;
}
long otherFile = getFileNumber(otherLsn);
if (myFile == otherFile) {
diff = Math.abs(getFileOffset(thisLsn) - getFileOffset(otherLsn));
} else if (myFile > otherFile) {
diff = calcDiff(myFile - otherFile,
logFileSize, thisLsn, otherLsn);
} else {
diff = calcDiff(otherFile - myFile,
logFileSize, otherLsn, thisLsn);
}
return diff;
}
/**
* Return the logsize in bytes between these two LSNs. This is an
* approximation; the logs might actually be a little more or less in
* size. This assumes that log files might have been cleaned.
*/
public static long getWithCleaningDistance(long thisLsn,
FileManager fileManager,
long otherLsn,
long logFileSize) {
long diff = 0;
assert thisLsn != NULL_LSN;
/* First figure out how many files lay between the two. */
long myFile = getFileNumber(thisLsn);
if (otherLsn == NULL_LSN) {
otherLsn = 0;
}
long otherFile = getFileNumber(otherLsn);
if (myFile == otherFile) {
diff = Math.abs(getFileOffset(thisLsn) - getFileOffset(otherLsn));
} else {
/* Figure out how many files lie between. */
Long[] fileNums = fileManager.getAllFileNumbers();
int myFileIdx = Arrays.binarySearch(fileNums,
new Long(myFile));
int otherFileIdx = Arrays.binarySearch(fileNums,
new Long(otherFile));
if (myFileIdx > otherFileIdx) {
diff = calcDiff(myFileIdx - otherFileIdx,
logFileSize, thisLsn, otherLsn);
} else {
diff = calcDiff(otherFileIdx - myFileIdx,
logFileSize, otherLsn, thisLsn);
}
}
return diff;
}
private static long calcDiff(long fileDistance,
long logFileSize,
long laterLsn,
long earlierLsn) {
long diff = fileDistance * logFileSize;
diff += getFileOffset(laterLsn);
diff -= getFileOffset(earlierLsn);
return diff;
}
/**
* @see LogReadable#logEntryIsTransactional.
*/
public boolean logEntryIsTransactionalX() {
return false;
}
/**
* @see LogReadable#getTransactionId
*/
public long getTransactionIdX() {
return 0;
}
}